#include "presentermonitor.h"
#include "libs/core/model/attentionmodel.h"
#include "libs/core/model/model.h"
#include "libs/core/model/presentermodel.h"
#include "libs/core/user/localuser.h"
#include "liverecorder.h"
#include "livestatusview.h"
#include "liveviewmanager.h"
#include "services/yingceservice.h"
#include "usersettings.h"
#include "yingce.h"
#include <QJsonDocument>
#include <QTimer>

namespace {
int toCount(const QString& s)
{
    static const QString unit = QStringLiteral("\344\270\207");
    if (s.contains(unit)) {
        QString t = s;
        t.remove(unit);
        return int(t.toFloat() * 10000);
    }
    return s.isEmpty() ? -1 : s.toInt();
}
}

PresenterMonitorWorker::PresenterMonitorWorker(LiveRecorder* recorder, QObject* parent)
    : QObject(parent), recorder_(recorder)
{
}

void PresenterMonitorWorker::start(int presenter)
{
    if (!done_) {
        return;
    }

    qDebug() << "Monitor presenter:" << presenter
             << YingCe::instance()->model()->presenterModel()->presenter(presenter)->nickname;

    presenter_ = presenter;
    done_ = false;

    // 首先从服务器获取状态
    YingCeService::instance()->getPresenterStatus(
            presenter,
            yc::ResponseHandler(
                    this, [this](const yc::Request& request, const yc::Response& response) {
                        Q_UNUSED(request)
                        // 已经处理了
                        if (done_ || presenter_ == 0) {
                            return;
                        }
                        // 处理响应
                        if (response.status() == yc::OK_CODE) {
                            // 主播正在直播
                            const yc::Presenter pr { response.data().toObject() };
                            if (pr.id == presenter_ && pr.status == yc::ACTIVE_LIVE_STATUS
                                && pr.updater != YingCe::instance()->localUser()->id()) {
                                qDebug() << "Monitor presenter: server status:" << pr.status
                                         << pr.active_live << pr.audience_count << pr.update_time
                                         << pr.updater;
                                // 更新主播状态
                                updatePresenterStatus(pr.status, pr.active_live, pr.audience_count,
                                                      pr.update_time, pr.updater);
                                return;
                            }
                        }
                        // 本地检测直播状态
                        if (auto p = YingCe::instance()->model()->presenterModel()->presenter(
                                    presenter_)) {
                            qDebug() << "Monitor presenter: get local live status";
                            if (!getLiveStatus(
                                        p->platform, p->platform_id,
                                        LiveHandler(this, &PresenterMonitorWorker::onGetStatus))) {
                                qDebug() << "Monitor presenter, failed to get local live status";
                                const auto now = QDateTime::currentDateTime();
                                updatePresenterStatus(yc::INACTIVE_LIVE_STATUS, 0, 0, now, 0);
                            }
                        } else {
                            done_ = true;
                        }
                    }));
}

void PresenterMonitorWorker::stop()
{
    qDebug() << "Monitor presenter: stop";
    int presenter = presenter_;
    presenter_ = 0;
    done_ = true;

    const auto& views = recorder_->liveViewManager()->statusView();
    for (auto p : std::as_const(views)) {
        if (p->presenter() == presenter) {
            p->unload();
            break;
        }
    }
}

void PresenterMonitorWorker::updatePresenterStatus(int status, int active_live, int audience_count,
                                                   const QDateTime& update_time, int updater)
{
    // 更新本地
    YingCe::instance()->model()->presenterModel()->setPresenterStatus(
            presenter_, status, active_live, audience_count, update_time, updater);

    // 更新远端
    if (status == yc::ACTIVE_LIVE_STATUS) {
        YingCeService::instance()->updatePresenterStatus(
                presenter_, status, active_live, audience_count,
                yc::ResponseHandler(this,
                                    [](const yc::Request& request, const yc::Response& response) {
                                        Q_UNUSED(request)
                                        if (response.status() != yc::OK_CODE) {
                                            qDebug() << response.status() << response.msg();
                                        }
                                    }));
    }

    done_ = true;
    presenter_ = 0;
}

bool PresenterMonitorWorker::getLiveStatus(int platform, const QString& presenter_platform_id,
                                           const LiveHandler& handler)
{
    const auto& views = recorder_->liveViewManager()->statusView();
    for (auto p : std::as_const(views)) {
        if (p->handled()) {
            qDebug() << "Monitor presenter: get local live status" << presenter_
                     << presenter_platform_id;
            p->getLiveStatus(presenter_, platform, presenter_platform_id, 30000, handler);
            return true;
        }
    }
    return false;
}

void PresenterMonitorWorker::onGetStatus(int presenter, const QString& args)
{
    Q_UNUSED(presenter)
    if (auto p = YingCe::instance()->model()->presenterModel()->presenter(presenter_)) {
        qDebug() << "Presenter monitor, get local live status result:" << presenter_ << presenter
                 << args;
        const auto now = QDateTime::currentDateTime();
        int count = toCount(args);
        if (count == -1) {
            // 更新主播状态
            updatePresenterStatus(yc::INACTIVE_LIVE_STATUS, 0, 0, now, 0);
        } else {
            updatePresenterStatus(yc::ACTIVE_LIVE_STATUS, p->active_live, count, now, 0);
        }
    }
}

PresenterRealtimeMonitorWorker::PresenterRealtimeMonitorWorker(LiveRecorder* recorder,
                                                               QObject* parent)
    : QObject(parent), recorder_(recorder)
{
    timer_ = new QTimer(this);
    timer_->setInterval(2500);
    connect(timer_, &QTimer::timeout, this, [this] {
        if (auto p = YingCe::instance()->model()->presenterModel()->presenter(presenter_)) {
            if (p->update_time.isNull()
                || p->update_time.msecsTo(QDateTime::currentDateTime()) > 5000) {
                YingCeService::instance()->getPresenterStatus(
                        presenter_,
                        yc::ResponseHandler(
                                this, [](const yc::Request& request, const yc::Response& response) {
                                    if (response.status() == yc::OK_CODE) {
                                        const yc::Presenter pr { response.data().toObject() };
                                        if (pr.status == yc::ACTIVE_LIVE_STATUS
                                            && pr.updater
                                                    != YingCe::instance()->localUser()->id()) {
                                            auto pm = YingCe::instance()->model()->presenterModel();
                                            pm->setPresenterStatus(pr.id, pr.status, pr.active_live,
                                                                   pr.audience_count,
                                                                   QDateTime::currentDateTime(),
                                                                   pr.updater);
                                        }
                                    }
                                }));
            }
        }
    });
}

void PresenterRealtimeMonitorWorker::start(int presenter)
{
    if (auto p = YingCe::instance()->model()->presenterModel()->presenter(presenter)) {
        bool ok = subscribe(presenter, p->platform, p->platform_id,
                            LiveHandler(this, &PresenterRealtimeMonitorWorker::onGetStatus));
        if (ok) {
            done_ = false;
            presenter_ = presenter;
            timer_->start();
        }
    }
}

void PresenterRealtimeMonitorWorker::stop()
{
    if (!done_) {
        unsubscribe(presenter_);
        timer_->stop();
        presenter_ = 0;
        done_ = true;
    }
}

bool PresenterRealtimeMonitorWorker::subscribe(int presenter, int platform,
                                               const QString& presenter_platform_id,
                                               const LiveHandler& handler)
{
    const auto& views = recorder_->liveViewManager()->realtimeViews();
    for (auto p : std::as_const(views)) {
        if (p->presenter() == 0) {
            p->getLiveStatus(presenter, platform, presenter_platform_id, 30000, handler);
            return true;
        }
    }
    return false;
}

void PresenterRealtimeMonitorWorker::unsubscribe(int presenter)
{
    const auto& views = recorder_->liveViewManager()->realtimeViews();
    for (auto p : std::as_const(views)) {
        if (p->presenter() == presenter) {
            p->unload();
            break;
        }
    }
}

void PresenterRealtimeMonitorWorker::onGetStatus(int presenter, const QString& args)
{
    auto pm = YingCe::instance()->model()->presenterModel();
    if (auto p = pm->presenter(presenter)) {
        auto now = QDateTime::currentDateTime();
        int count = toCount(args);
        if (count == -1) {
            pm->setPresenterStatus(p, yc::INACTIVE_LIVE_STATUS, 0, 0, now, 0);
        } else {
            pm->setPresenterStatus(p, yc::ACTIVE_LIVE_STATUS, p->active_live, count, now, 0);

            // 比较可贵，更新远端
            YingCeService::instance()->updatePresenterStatus(
                    presenter, yc::ACTIVE_LIVE_STATUS, p->active_live, count,
                    yc::ResponseHandler(
                            this, [](const yc::Request& request, const yc::Response& response) {
                                Q_UNUSED(request)
                                if (response.status() != yc::OK_CODE) {
                                    qDebug() << response.status() << response.msg();
                                }
                            }));
        }
    }
}

PresenterMonitor::PresenterMonitor(LiveRecorder* recorder, QObject* parent)
    : QObject { parent }, recorder_(recorder)
{
    int realtime_count = UserSettings::instance().recorderSettings().realtime_monitor_count;
    for (int i = 0; i < realtime_count; ++i) {
        realtime_workers_.push_back(new PresenterRealtimeMonitorWorker(recorder, this));
    }

    int regular_count = UserSettings::instance().recorderSettings().regular_monitor_count;
    for (int i = 0; i < regular_count; ++i) {
        workers_.push_back(new PresenterMonitorWorker(recorder, this));
    }

    int interval = 30000 / regular_count;
    if (interval < 10000) {
        interval = 10000;
    }
    timer_ = new QTimer(this);
    timer_->setInterval(interval);
    connect(timer_, &QTimer::timeout, this, &PresenterMonitor::getLiveStatus);

    connect(YingCe::instance()->model()->attentionModel(), &yc::AttentionModel::added, this,
            &PresenterMonitor::getLiveStatus);
    connect(YingCe::instance()->model()->attentionModel(), &yc::AttentionModel::removed, this,
            &PresenterMonitor::removeAttention);
}

void PresenterMonitor::start()
{
    started_ = true;
    timer_->start();

    int count = 0;
    auto am = YingCe::instance()->model()->attentionModel();
    for (const auto a : am->attentions()) {
        if (a->auto_check) {
            setRealtime(a->presenter, true);
            if (++count >= realtime_workers_.size()) {
                break;
            }
        }
    }
}

void PresenterMonitor::stop()
{
    started_ = false;
    timer_->stop();

    for (auto p : std::as_const(realtime_workers_)) {
        p->stop();
    }

    for (auto p : std::as_const(workers_)) {
        p->stop();
    }
}

void PresenterMonitor::setRealtime(int presenter, bool realtime)
{
    if (realtime) {
        for (int i = 0; i < realtime_workers_.size(); ++i) {
            if (realtime_workers_.at(i)->isDone()) {
                realtime_workers_.at(i)->start(presenter);
                break;
            }
        }
    } else {
        for (int i = 0; i < realtime_workers_.size(); ++i) {
            if (realtime_workers_.at(i)->presenter() == presenter) {
                realtime_workers_.at(i)->stop();
                break;
            }
        }
    }
}

void PresenterMonitor::getLiveStatus()
{
    if (!started_)
        return;

    auto am = YingCe::instance()->model()->attentionModel();
    auto pm = YingCe::instance()->model()->presenterModel();
    const int count = am->attentions().size();
    if (count > 0) {
        if (pos_ >= count) {
            pos_ = 0;
        }

        const auto attention = am->attentions().at(pos_);
        if (attention->auto_check) {
            bool found = false;
            for (int i = 0; i < realtime_workers_.size(); ++i) {
                if (realtime_workers_.at(i)->presenter() == attention->presenter) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                setRealtime(attention->presenter, true);
            }
            ++pos_;
        } else {
            if (const auto presenter = pm->presenter(attention->presenter)) {
                const auto now = QDateTime::currentDateTime();
                const int frequency =
                        UserSettings::instance().recorderSettings().regular_monitor_frequency * 60;
                if (presenter->update_time.isNull()
                    || presenter->update_time.secsTo(now) > frequency) {
                    bool ok = false;
                    for (int i = 0; i < workers_.size(); ++i) {
                        if (workers_.at(i)->isDone()) {
                            ok = true;
                            workers_.at(i)->start(presenter->id);
                            ++pos_;
                            break;
                        }
                    }
                    if (!ok) {
                        pm->setPresenterStatus(presenter, yc::INACTIVE_LIVE_STATUS, 0, 0, now, 0);
                    }
                } else {
                    ++pos_;
                }
            }
        }
    }
}

void PresenterMonitor::removeAttention(yc::Attention* attention)
{
    if (attention->auto_check) {
        for (auto p : std::as_const(realtime_workers_)) {
            if (p->presenter() == attention->presenter) {
                p->stop();
                break;
            }
        }
    } else {
        for (auto p : std::as_const(workers_)) {
            if (p->presenter() == attention->presenter) {
                p->stop();
                break;
            }
        }
    }
}
